package edu.buffalo.cse.cse486_586.simpledynamo;

import java.util.List;

import edu.buffalo.cse.cse486_586.simpledynamo.R;
import edu.buffalo.cse.cse486_586.simpledynamo.util.AppUtil;
import edu.buffalo.cse.cse486_586.simpledynamo.util.CommonUtil;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;

public class MainActivity extends Activity {
	// Debugging
	private static final String TAG = "MainActivity";
	private static final boolean D = true;

	// Layout Views
	private ListView msgList;
	// required button
	private Button put1Button;
	private Button put2Button;
	private Button put3Button;
	private Button getButton;
	private Button dumpButton;
	// self defined button
	private Button debugButton;
	private Button cleanButton;

	// Array adapter for the conversation thread
	private ArrayAdapter<String> msgListAdapter;
	// helper && Thread
	private Thread listeningThread;
	private DynamoHelper dHelper;
	final Handler messageHandler = new Handler();
//	private final ControllerThread updateUI = new ControllerThread(this);

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// binding activity with service
		setupChatView();
		// set up config
		dHelper = new DynamoHelper();
		int lp = getConsolePort();
		dHelper.setLocalPort(lp);
		dHelper.setLocalRedirPort(AppUtil.getRedirPortByConsolePort(lp));
		dHelper.setLocalHashPort(CommonUtil.genHash(String.valueOf(lp)));
		listeningThread = new Thread(new ListeningThread(this));
		listeningThread.start();
	}

	@Override
	public void onStart() {
		super.onStart();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
	}

	/*
	 * clean list view, but not delete the data in provider
	 */
	public void cleanScreen() {
		msgListAdapter.clear();
	}

	/*
	 * set up UI interface once the activity start.
	 */
	private void setupChatView() {
		// view list
		msgList = (ListView) findViewById(R.id.msgList);
		msgListAdapter = new ArrayAdapter<String>(this, R.layout.list_msg);
		try {
			// insertData();
			queryData();
		} catch (Exception e) {
			Log.d(TAG, "Exception", e);
		}
		msgList.setAdapter(msgListAdapter);

		// required button
		put1Button = (Button) findViewById(R.id.put1tButton);
		put1Button.setOnClickListener(new PutListener("PUT1"));
		put2Button = (Button) findViewById(R.id.put2Button);
		put2Button.setOnClickListener(new PutListener("PUT2"));
		put3Button = (Button) findViewById(R.id.put3Button);
		put3Button.setOnClickListener(new PutListener("PUT3"));

		getButton = (Button) findViewById(R.id.getButton);
		getButton.setOnClickListener(new GetListener());
		dumpButton = (Button) findViewById(R.id.dumpButton);
		dumpButton.setOnClickListener(new DumpListener());
		// self defined button
		debugButton = (Button) findViewById(R.id.debugButton);
		debugButton.setOnClickListener(new DebugListener());
		cleanButton = (Button) findViewById(R.id.cleanButton);
		cleanButton.setOnClickListener(new CleanListener());
	}

	public void display(String key, String value, String version) {
		String out = "key :" + key + "\nvalue :" + value + "\nversion :"
				+ version;
		msgListAdapter.add(out);
	}

	private void queryData() {
		Cursor c = getContentResolver().query(
				AppUtil.CONTENT_URI,
				new String[]{AppUtil.KeyValueTable.COL_ID,
						AppUtil.KeyValueTable.COL_PROVIDER_KEY,
						AppUtil.KeyValueTable.COL_PROVIDER_VALUE,
						AppUtil.KeyValueTable.COL_PROVIDER_VERSION}, null,
				null, null);

		if (c.moveToFirst()) {
			while (!c.isAfterLast()) {
				String out = "";
				out += "key : "
						+ c.getString(c
								.getColumnIndexOrThrow(AppUtil.KeyValueTable.COL_PROVIDER_KEY))
						+ "\n";
				out += "value : "
						+ c.getString(c
								.getColumnIndexOrThrow(AppUtil.KeyValueTable.COL_PROVIDER_VALUE))
						+ "\n";
				out += "version : "
						+ c.getString(c
								.getColumnIndexOrThrow(AppUtil.KeyValueTable.COL_PROVIDER_VERSION));
				msgListAdapter.add(out);
				c.moveToNext();
			}
		}
	}

	public void purposeValue(String key, String value) {
		List<Integer> preferList = dHelper.getPreferenceList(CommonUtil
				.genHash(key));
		String vNum = String.valueOf(System.currentTimeMillis());

		if (dHelper.getLocalRedirPort() == preferList.get(0)) {
			commit(key, value, vNum);
		} else {
			sendToCoordinator(key, value, vNum);
		}
	}

	private void sendToCoordinator(String key, String value, String vNum) {
		String pkg = AppUtil.PACKAGE_INSERT + AppUtil.PACKAGE_DELIMITER + key
				+ AppUtil.PACKAGE_DELIMITER + value + AppUtil.PACKAGE_DELIMITER
				+ vNum;

		// send preparing pkg to all replicas
		List<Integer> preferList = dHelper.getPreferenceList(CommonUtil
				.genHash(key));
		if (preferList.size() > 0) {
			for (int i = 0; i < preferList.size(); i++) {
				if (dHelper.uniCast(preferList.get(i), pkg, false)) {
					break;
				} else {
					String hashString = dHelper
							.getHasStringByRedirctPort(preferList.get(i));
					dHelper.changeGroupView(hashString, false);
					// boardcast failure
					// TODO
					dHelper.sendFailurePkg(hashString);
				}
			}
		}
	}

	public void sendCommitPkg(String key, String value, String version) {
		String pkg = AppUtil.PACKAGE_COMMIT + AppUtil.PACKAGE_DELIMITER + key
				+ AppUtil.PACKAGE_DELIMITER + value + AppUtil.PACKAGE_DELIMITER
				+ version;

		// send preparing pkg to all replicas
		List<Integer> preferList = dHelper.getPreferenceList(CommonUtil
				.genHash(key));
		for (Integer port : preferList) {
			if (port != dHelper.getLocalRedirPort()) {
				if (!dHelper.uniCast(port, pkg, false)) {
					String hashString = dHelper.getHasStringByRedirctPort(port);
					dHelper.changeGroupView(hashString, false);
					// boardcast failure
					dHelper.sendFailurePkg(hashString);
				}
			}
		}
	}

	public void commit(String key, String value, String vNum) {
		String oldVserion = isExist(CommonUtil.genHash(key));

		if (oldVserion.equals(AppUtil.KEY_NONE_EXIST)) {
			// insert new key-value
			ContentValues cv = new ContentValues();
			cv.put(AppUtil.KeyValueTable.COL_PROVIDER_KEY,
					CommonUtil.genHash(key));
			cv.put(AppUtil.KeyValueTable.COL_PROVIDER_VALUE, value);
			cv.put(AppUtil.KeyValueTable.COL_PROVIDER_VERSION, vNum);
			getContentResolver().insert(AppUtil.CONTENT_URI, cv);
		} else if (Long.valueOf(vNum) > Long.valueOf(oldVserion)) {
			// update key-value
			ContentValues cv = new ContentValues();
			cv.put(AppUtil.KeyValueTable.COL_PROVIDER_KEY,
					CommonUtil.genHash(key));
			cv.put(AppUtil.KeyValueTable.COL_PROVIDER_VALUE, value);
			cv.put(AppUtil.KeyValueTable.COL_PROVIDER_VERSION, vNum);
			// uri, values, where, selectionArgs
			getContentResolver().update(
					AppUtil.CONTENT_URI,
					cv,
					AppUtil.KeyValueTable.COL_PROVIDER_KEY + "='"
							+ CommonUtil.genHash(key) + "'", null);
		}
	}

	public String isExist(String key) {
		Cursor c = getContentResolver().query(
				AppUtil.CONTENT_URI,
				new String[]{AppUtil.KeyValueTable.COL_ID,
						AppUtil.KeyValueTable.COL_PROVIDER_KEY,
						AppUtil.KeyValueTable.COL_PROVIDER_VALUE,
						AppUtil.KeyValueTable.COL_PROVIDER_VERSION},
				AppUtil.KeyValueTable.COL_PROVIDER_KEY + "='" + key + "'",
				null, null);

		if (c.moveToFirst()) {
			String version = c
					.getString(c
							.getColumnIndexOrThrow(AppUtil.KeyValueTable.COL_PROVIDER_VERSION));
			return version;
		} else {
			return AppUtil.KEY_NONE_EXIST;
		}
	}

	public int getConsolePort() {
		TelephonyManager tel = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		String portStr = tel.getLine1Number().substring(
				tel.getLine1Number().length() - 4);

		return Integer.parseInt(portStr);
	}

	private class PutListener implements OnClickListener {
		private String valueString;

		public PutListener(String str) {
			this.valueString = str;
		}

		@Override
		public void onClick(View v) {
			for (int i = 0; i < 10; i++) {
				purposeValue(String.valueOf(i), valueString + i);
			}
		}
	}

	private class GetListener implements OnClickListener {
		@Override
		public void onClick(View v) {
			// TODO start get operation
			for (int i = 0; i < 10; i++) {
				String key = String.valueOf(i);

				String pkg = AppUtil.PACKAGE_GET + AppUtil.PACKAGE_DELIMITER
						+ dHelper.getLocalRedirPort()
						+ AppUtil.PACKAGE_DELIMITER + key;

				// send preparing pkg to all replicas
				List<Integer> preferList = dHelper.getPreferenceList(CommonUtil
						.genHash(key));

				for (int j = 0; j < preferList.size(); j++) {
					if (preferList.get(j) != dHelper.getLocalRedirPort()) {
						if (dHelper.uniCast(preferList.get(j), pkg, false)) {
							break;
						} else {
							String hashString = dHelper
									.getHasStringByRedirctPort(preferList
											.get(j));
							dHelper.changeGroupView(hashString, false);
							// boardcast failure
							dHelper.sendFailurePkg(hashString);
						}
					}
				}
			}
		}
	}

	private class DumpListener implements OnClickListener {
		@Override
		public void onClick(View v) {
			queryData();
		}
	}

	private class CleanListener implements OnClickListener {
		@Override
		public void onClick(View v) {
			getContentResolver().delete(AppUtil.CONTENT_URI, null, null);
			cleanScreen();
		}
	}

	private class DebugListener implements OnClickListener {
		@Override
		public void onClick(View v) {
			// delete all k-v in content provider
			msgListAdapter.clear();
			// dHelper.broadcastPkg();
		}
	}

	public void postPkg(String pkg) {
		this.msgListAdapter.add(pkg);
	}

//	public ControllerThread getUpdateUI() {
//		return updateUI;
//	}
	public Handler getMessageHandler() {
		return messageHandler;
	}

	public String lookUp(String key) {
		String result = "";
		Cursor c = getContentResolver().query(
				AppUtil.CONTENT_URI,
				new String[]{AppUtil.KeyValueTable.COL_ID,
						AppUtil.KeyValueTable.COL_PROVIDER_KEY,
						AppUtil.KeyValueTable.COL_PROVIDER_VALUE,
						AppUtil.KeyValueTable.COL_PROVIDER_VERSION},
				AppUtil.KeyValueTable.COL_PROVIDER_KEY + "='"
						+ CommonUtil.genHash(key) + "'", null, null);

		if (c.moveToFirst()) {
			String hashKey = c
					.getString(c
							.getColumnIndexOrThrow(AppUtil.KeyValueTable.COL_PROVIDER_KEY));
			String value = c
					.getString(c
							.getColumnIndexOrThrow(AppUtil.KeyValueTable.COL_PROVIDER_VALUE));
			String version = c
					.getString(c
							.getColumnIndexOrThrow(AppUtil.KeyValueTable.COL_PROVIDER_VERSION));

			result = "key : " + hashKey + "\n value : " + value
					+ "\n version : " + version;
		}

		return result;
	}

	public void sendGetResponsePkg(String redir, String str) {
		String pkg = AppUtil.PACKAGE_GET_RESPONSE + AppUtil.PACKAGE_DELIMITER + str;
		
		if (!dHelper.uniCast(Integer.parseInt(redir), pkg, true)) {
			String hashString = dHelper.getHasStringByRedirctPort(Integer
					.parseInt(str));
			dHelper.changeGroupView(hashString, false);
			// boardcast failure message
			dHelper.sendFailurePkg(hashString);
		}
	}
	
	public synchronized void addToListView(String item){
		msgListAdapter.add(item);
	}
	
	public void findFailur(String hashKey){
		dHelper.changeGroupView(hashKey, false);
	}
}